/*
 * bigrub.java
 *
 * Created on July 22, 2004, 2:27 PM
 */

package com.milowski.monos.tools;

import java.io.*;
import java.util.*;
import java.util.logging.*;
import java.math.*;
import com.milowski.monos.monomial.*;
import com.milowski.monos.binomial.*;

/**
 *
 * @author  R. Alexander Milowski
 */
public class bigrub {
   
static String fineLog = 
"handlers= java.util.logging.ConsoleHandler\n"+
".level= FINE\n"+
"java.util.logging.ConsoleHandler.level = FINE\n"+
"java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter\n";
;
static String finerLog = 
"handlers= java.util.logging.ConsoleHandler\n"+
".level= FINER\n"+
"java.util.logging.ConsoleHandler.level = FINER\n"+
"java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter\n";
;
static String finestLog = 
"handlers= java.util.logging.ConsoleHandler\n"+
".level= FINEST\n"+
"java.util.logging.ConsoleHandler.level = FINEST\n"+
"java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter\n";
;
   public static void setLogLevel(Level level) {
      if (level==Level.FINE) {
         try {
            LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(fineLog.getBytes()));
         } catch (java.io.IOException ex) {
            ex.printStackTrace();
         }
      } else if (level==Level.FINER) {
         try {
            LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(finerLog.getBytes()));
         } catch (java.io.IOException ex) {
            ex.printStackTrace();
         }
      } else if (level==Level.FINEST) {
         try {
            LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(finestLog.getBytes()));
         } catch (java.io.IOException ex) {
            ex.printStackTrace();
         }
         
      }
   }

   public static void warm(int threshold,boolean timing) {
      short [] b1 = {-1,1,1,-1,1,-1,-1,1,0,0,0,0,0,0,0,0};
      short [] b2 = {-1,1,1,-1,0,0,0,0,1,-1,-1,1,0,0,0,0};
      short [] b3 = {-1,1,0,0,1,-1,0,0,1,-1,0,0,-1,1,0,0};
      short [] b4 = {-1,1,0,0,1,-1,0,0,0,0,1,-1,0,0,-1,1};
      short [] b5 = {-1,0,1,0,1,0,-1,0,1,0,-1,0,-1,0,1,0};
      short [] rowVector = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
      LexOrder lexOrder = new LexOrder();
      lexOrder.add("x1");
      lexOrder.add("x2");
      lexOrder.add("x3");
      lexOrder.add("x4");
      lexOrder.add("x5");
      lexOrder.add("x6");
      lexOrder.add("x7");
      lexOrder.add("x8");
      lexOrder.add("x9");
      lexOrder.add("x10");
      lexOrder.add("x11");
      lexOrder.add("x12");
      lexOrder.add("x13");
      lexOrder.add("x14");
      lexOrder.add("x15");
      lexOrder.add("x16");
      long loopStart = System.currentTimeMillis();
      for (int i=0; i<threshold; i++) {
         long start = System.currentTimeMillis();
         LexOrderedList initial = new LexOrderedList(lexOrder);
         initial.add(new FullBinomial(b1));
         initial.add(new FullBinomial(b2));
         initial.add(new FullBinomial(b3));
         initial.add(new FullBinomial(b4));
         initial.add(new FullBinomial(b5));
         GroebnerBasis basis = new GroebnerBasis(lexOrder);
         basis.computeToricIdeal(initial,rowVector);
         if (timing) {
            long end = System.currentTimeMillis();
            System.err.println("Warming basis elapsed: "+(end-start));
         }
      }
      if (timing) {
         long end = System.currentTimeMillis();
         System.err.println("Warming elapsed: "+(end-loopStart));
         System.err.flush();
      }
   }
   /** Creates a new instance of bigrub */
   public bigrub() {
   }

   public static void usage() {
      System.err.println("com.milowski.monos.tools.bigrub { -l fine|finer|finest | -m (lex|revlex|degrevlex) | -r n,n,n,... } matrix-file");
   }
   /**
    * @param args the command line arguments
    */
   public static void main(String[] args) {
      int argidx = 0;
      int [] reorder = null;
      int [] inputReorder = null;
      int [] outputReorder = null;
      String [] sweights = null;
      int [] weights = null;
      GroebnerBasis.Ordering ordering = null; 
      boolean toric = false;
      boolean timing = false;
      boolean compact = false;
      boolean autoreduce = true;
      boolean compactOutput = true;
      boolean reverseInput = false;
      boolean weightedLex = false;
      boolean needWeights = false;
      boolean transpose = false;
      boolean echoInput = false;
      boolean rowVectorSet = false;
      int warmThreshold = 20;
      boolean warm = false;
      int loopNumber = 1;
      int [] rowVector = null;
      while (argidx<args.length && args[argidx].charAt(0)=='-') {
         if (args[argidx].equals("-m")) {
            if (ordering!=null) {
               System.err.println("Only one ordering can be set for the computation.");
            }
            argidx++;
            if (argidx==args.length) {
               System.err.println("The -m parameter requires an argument of 'lex', 'revlex', or 'degrevlex'.");
               System.exit(1);
            }
            if (args[argidx].equals("lex")) {
               ordering = GroebnerBasis.LEXICOGRAPHIC;
            } else if (args[argidx].equals("revlex")) {
               ordering = GroebnerBasis.REVERSE_LEXICOGRAPHIC;
            } else if (args[argidx].equals("degrevlex")) {
               ordering = GroebnerBasis.DEGREE_REVERSE_LEXICOGRAPHIC;
            } else if (args[argidx].equals("wdegrevlex")) {
               weightedLex = false;
               needWeights = true;
            } else if (args[argidx].equals("wlex")) {
               weightedLex = true;
               needWeights = true;
            } else {
               System.err.println("Ordering "+args[argidx]+" is not known.");
               System.exit(1);
            }
         } else if (args[argidx].equals("-l")) {
            argidx++;
            if (argidx==args.length) {
               System.err.println("The -l parameter requires an argument of 'fine', 'finer', or 'finest'.");
               System.exit(1);
            }
            if (args[argidx].equals("fine")) {
               setLogLevel(Level.FINE);
            } else if (args[argidx].equals("finer")) {
               setLogLevel(Level.FINER);
            } else if (args[argidx].equals("finest")) {
               setLogLevel(Level.FINEST);
            }
         } else if (args[argidx].equals("-reorder")) {
            argidx++;
            if (argidx==args.length) {
               System.err.println("The -reorder parameter requires an argument of the indexes of the reordering.");
               System.exit(1);
            }
            String [] nums = args[argidx].split(",");
            reorder = new int[nums.length];
            for (int i=0; i<nums.length; i++) {
               reorder[i] = Integer.parseInt(nums[i]);
            }
         } else if (args[argidx].equals("-input-reorder")) {
            argidx++;
            if (argidx==args.length) {
               System.err.println("The -input-reorder parameter requires an argument of the indexes of the reordering.");
               System.exit(1);
            }
            if (args[argidx].equals("reverse")) {
               reverseInput = true;
            } else {
               String [] nums = args[argidx].split(",");
               inputReorder = new int[nums.length];
               outputReorder = new int[nums.length];
               for (int i=0; i<nums.length; i++) {
                  inputReorder[i] = Integer.parseInt(nums[i]);
                  outputReorder[inputReorder[i]] = i;
               }
            }
         } else if (args[argidx].equals("-w")) {
            argidx++;
            if (argidx==args.length) {
               System.err.println("The -w parameter requires an argument of the weights for the final basis computation.");
               System.exit(1);
            }
            sweights = args[argidx].split(",");
         } else if (args[argidx].equals("-r")) {
            argidx++;
            if (argidx==args.length) {
               System.err.println("The -r parameter requires an argument of the positive row space vector.");
               System.exit(1);
            }
            String [] nums = args[argidx].split(",");
            rowVector = new int[nums.length];
            for (int i=0; i<nums.length; i++) {
               rowVector[i] = Integer.parseInt(nums[i]);
               if (rowVector[i]==0) {
                  System.err.println("The column sum for column "+i+" is zero.  You must specify a positive row space vector.");
                  System.exit(1);
               }
            }
            rowVectorSet = true;
         } else if (args[argidx].equals("-cost")) {
            argidx++;
            if (argidx==args.length) {
               System.err.println("The -cost parameter requires a file name argument.");
               System.exit(1);
            }
            try {
               FileReader input = new FileReader(args[argidx]);
               MatrixBasis costMatrix = MatrixBasis.read(input,reverseInput,inputReorder);
               input.close();
               int width = costMatrix.getColumnCount();
               weights = new int[width];
               for (int i=0; i<width; i++) {
                  weights[i] = costMatrix.get(0,i);
               }
            } catch (IOException ex) {
               System.err.println("I/O error: "+ex.getMessage());
               System.exit(1);
            }
         } else if (args[argidx].equals("-t")) {
            toric = true;
         //} else if (args[argidx].equals("-c")) {
         //   compact = true;
         } else if (args[argidx].equals("-no-autoreduce")) {
            autoreduce = false;
         } else if (args[argidx].equals("-full-output")) {
            compactOutput = true;
         } else if (args[argidx].equals("-timing")) {
            timing = true;
         } else if (args[argidx].equals("-transpose")) {
            transpose = true;
         } else if (args[argidx].equals("-echo-input")) {
            echoInput = true;
         } else if (args[argidx].equals("-warm")) {
            warm = true;
         } else if (args[argidx].equals("-n")) {
            argidx++;
            if (argidx==args.length) {
               System.err.println("The -n parameter requires an argument of the number of times to compute.");
               System.exit(1);
            }
            loopNumber = Integer.parseInt(args[argidx]);
         }
         argidx++;
      }
      if ((args.length-argidx)<1) {
         usage();
         System.exit(1);
      }
      if (warm) {
         warm(warmThreshold,timing);
      }
      while (argidx<args.length) {
         try {
            if (!rowVectorSet) {
               rowVector = null;
            }
            //setLogLevel(Level.FINE);
            FileReader input = new FileReader(args[argidx]);
            LexOrderedList initial;
            if (toric) {
//               Matrix<BigInteger> matrix = new Matrix<BigInteger>();
//               matrix.read(input,NumberConstructor.BIG_INTEGER);
               MatrixBasis m = MatrixBasis.read(input,reverseInput,inputReorder);
               if (transpose) {
                  m.transpose();
               }
               if (echoInput) {
                  System.out.println("Input:");
                  m.write(System.out);
                  System.out.println();
               }

               int width = m.getColumnCount();
               if (rowVector==null) {
                  rowVector = new int[width];
                  int rowCount = m.getRowCount();
                  for (int i=0; i<width; i++) {
                     rowVector[i] = 0;
                     for (int j=0; j<rowCount; j++) {
                        int entry = m.get(j,i);
                        //System.out.println(j+","+i+"="+entry);
                        rowVector[i] += entry;
                     }
                     //rowVector[i] = 1;
                  }
                  for (int i=0; i<width; i++) {
                     if (rowVector[i]==0) {
                        System.err.println("The column sum for column "+i+" is zero.  You must specify a positive row space vector.");
                        System.exit(1);
                     }
                  }
               }
               if (width!=rowVector.length) {
                  System.err.println("The row vector is not the same size as the number of columns.");
                  System.exit(1);
               }
               MatrixBasis kernel = KernelBasis.getDefaultInstance().compute(m);
//               MatrixBasis kernel = KernelBasis.getDefaultInstance().compute(matrix);
               if (kernel==null) {
                  System.err.println("Kernel is empty.  No basis can be computed.");
                  System.exit(1);
               }
               initial = kernel.toBinomials(compact);
               //initial = MatrixBasis.readMatrixAsBinomials(input,compact,reverseInput,inputReorder);
            } else {
               initial = MatrixBasis.readMatrixAsBinomials(input,false,reverseInput,inputReorder);
            }
            input.close();
            LexOrder order = initial.getLexOrder();

            int width = order.size();
            GroebnerBasis basis = new GroebnerBasis(order);
            if (reorder!=null) {
               if (order.size()!=reorder.length) {
                  System.err.println("Re-order length is not the same as the input columns.");
                  System.exit(1);
               }
               basis.setExponentReOrdering(reorder);
            }
            if (needWeights && weights==null) {
               if (sweights==null) {
                  System.err.println("Weights need to be specified (e.g. -cost or -w).");
                  System.exit(1);
               }
               weights = new int[order.size()];
               int index = 0;
               for (int i=0; i<sweights.length; i++) {
                  if (index>=weights.length) {
                     System.err.println("Weights length is not the same as the input columns.");
                     System.exit(1);
                  }
                  //System.out.println("Processing: "+sweights[i]);
                  int colon = sweights[i].indexOf(':');
                  if (colon>0) {
                     int number = Integer.parseInt(sweights[i].substring(0,colon));
                     int value = Integer.parseInt(sweights[i].substring(colon+1));
                     if ((index+number)>weights.length) {
                        System.err.println("Weight spec "+sweights[i]+" makes the weight vector longer than the number of columns.");
                        System.exit(1);
                     }
                     for (int j=0; j<number; j++) {
                        weights[index++] = value;
                     }
                  } else {
                     weights[index++] = Integer.parseInt(sweights[i]);
                  }
               }
            }
            if (ordering==null && needWeights) {
               ordering = weightedLex ? 
                  (GroebnerBasis.Ordering)new GroebnerBasis.WeightedLexicographicOrdering(weights) :
                  (GroebnerBasis.Ordering)new GroebnerBasis.WeightedReverseLexicographicOrdering(weights);
            }
            if (ordering!=null) {
               basis.setMonomialOrdering(ordering);
            }
            basis.setAutoReduce(autoreduce);
            long start = System.currentTimeMillis();
            if (toric) {
               for (int i=0; i<loopNumber; i++) {
                  LexOrderedList copy = null;
                  if ((i+1)<loopNumber) {
                     copy = new LexOrderedList(initial.getLexOrder());
                     for (Iterator elements = initial.iterator();
                          elements.hasNext(); ) {
                        FullBinomial b = (FullBinomial)elements.next();
                        copy.add(new FullBinomial(b.leading,b.trailing));
                     }
                  }
                  long loopStart = System.currentTimeMillis();
                  basis.computeToricIdeal(initial,rowVector);
                  long end = System.currentTimeMillis();
                  if (timing) {
                     System.err.println("Elapsed: "+(end-loopStart));
                     System.err.flush();
                  }
                  initial = copy;
               }
               /*
            } else if (compact) {
               basis.computeCompactBasis(initial);
                */
            } else {
               for (int i=0; i<loopNumber; i++) {
                  LexOrderedList copy = null;
                  if ((i+1)<loopNumber) {
                     copy = new LexOrderedList(initial.getLexOrder());
                     for (Iterator elements = initial.iterator();
                          elements.hasNext(); ) {
                        FullBinomial b = (FullBinomial)elements.next();
                        copy.add(new FullBinomial(b.leading,b.trailing));
                     }
                  }
                  long loopStart = System.currentTimeMillis();
                  basis.computeFullBasis(initial);
                  long end = System.currentTimeMillis();
                  if (timing) {
                     System.err.println("Elapsed: "+(end-loopStart));
                     System.err.flush();
                  }
                  initial = copy;
               }
            }
            long end = System.currentTimeMillis();
            Writer w = new OutputStreamWriter(System.out);
            if (reverseInput) {
               outputReorder = new int[width];
               for (int i=0; i<outputReorder.length; i++) {
                  outputReorder[i] = outputReorder.length-i-1;
               }
            }
            basis.writeAsMatrix(w,compact||compactOutput,outputReorder);
            w.flush();
            if (timing) {
               long diffTime = end-start;
               if (loopNumber>1) {
                  System.err.println("Average: "+diffTime/loopNumber);
               }
            }
         } catch (java.io.FileNotFoundException ex) {
            System.out.println("Cannot find file: "+ex.getMessage());
            System.exit(1);
         } catch (java.io.IOException ex) {
            ex.printStackTrace();
            System.exit(1);
         }
         argidx++;
      }
      System.exit(0);
   }
   
}
